/*
 * Brute Force MD5 - Alogrithme simple uniquement dans la table ASCII [A-Za-z0-9]
 * 
 * Author Tellier Vincent
 * Date 01/02/2010
 * 
 */

#include <stdio.h>		// Entrées/sorties standards.
#include <string.h>		// Pour avoir strcpy
#include "bruteforce.h"	// Notre lib de types
#include "Simple.h"		// L'en-tête de ce fichier
#include "md5.h"		// Pour générer les md5

void * AlgoSimple(void * args)
{
	t_bruteforce infos			= *((t_bruteforce *) args);
	char *		checkSum		= infos.checksum;
	int			maxLength		= infos.max_length;
	pthread_mutex_t * my_mut	= &((*((t_bruteforce *) args)).mutal_ex);
	char *		myStr			= (char *) malloc(maxLength * sizeof(char));
	int *		my_cs_inds		= infos.ch_index;
	int			myStrLength		= 1;
	int			termine			= FALSE;
	char *		retour;

	// Initialisation de la première chaine à tester
	myStr = infos.value_at_start;

	printf("valeur de départ : %s\n",myStr);

	printf("Bloqué\n");

	// On tente de vérrouiller le mutex pour bloquer l'execution ici
	// et ainsi attendre le feu vert du thread de contrôle.
	pthread_mutex_lock(my_mut);
	printf("Débloqué...\t");
	pthread_mutex_unlock(my_mut);
	printf("mutex déverouillé.\n");
	
	// Début de la recherche
	while(md5Compare(checkSum, myStr, myStrLength) == FALSE && termine == FALSE)
	{
		// Trace d'exécution
	//	printf("%s\t%d\n", myStr, *(infos.is_ended));
		termine = nextStr(myStr, &myStrLength, 0, maxLength, infos.jump, infos.charset, strlen(infos.charset),my_cs_inds) == TRUE || *(infos.is_ended) == TRUE;
	}
	// Trace d'exécution
//	printf("%s\n", myStr);
	printf("Fin de recherche\n");

	if(termine == TRUE)	// La clef n'a pas été trouvée
	{
		if(*(infos.is_ended) == FALSE)
			printf("Non trouvé.\n\n");
		else
			printf("Un concurrent à trouvé.\n");
	}
	else	// La clef à été trouvée
	{
		*(infos.result) = myStr;
		*(infos.is_ended) = TRUE;
	}
	return NULL;
}

/**
 * Récupération de la chaine suivante à tester.
 * @param	myStr		La combinaison précédente.
 * @param	myStrLength	La taille de la combinaison précédente
 * @param	indice		L'indice du caractère à modifier dans la combinaison précédente
 * @param	maxLength	La taille maximale autorisée pour la combinaison
 * @param	jump		Le nombre de caractères du charset à sauter pour atteindre la combinaison suivante
 * @param	charset		L'adresse du charset
 * @param	size_cs		La taille du charset
 * @param	cs_indice	Le t'ableau d'indices des caractères de la combinaison précédente, indices par rapport au charset.
 * 
 * @return TRUE si on doit arrêter la recherche (nombre maximal de caractères atteint).
 */
int nextStr(char * myStr, int * myStrLength, int indice, int maxLength, int jump, char * charset, int size_cs, int * cs_indice)
{
	// Variables locales
	int retour;		// La valeur de retour de la fonction
	int nb_loop;	// Le nombre de boucles effectuées sur le charset lors du passage au caractère suivant
	int next_i_cs;	// L'indice du caractère suivant au sein du charset

	// Initialisation des variables locales
	retour		= FALSE;								// Par défaut, on a pas terminé la recherche.
	nb_loop		= (cs_indice[indice] + jump)/size_cs;	// Le nombre de boucles est la division euclidienne de
														// l'indice précédent plus le saut à effectuer par la taille du charset
	next_i_cs	= (cs_indice[indice] + jump)%size_cs;	// L'indice du caractère suivant dans le charset est le modulo.

	// Dans tous les cas, on passe au caractère suivant
	myStr[indice]		= charset[next_i_cs];	// On récupère le caractère de l'indice suivant dans le charset.
	cs_indice[indice]	= next_i_cs;			// On met à jour le tableau d'indices.

	// Tant que l'on dépasse la taille du charset, on doit "incrémenter" le caractère suivant.
	while(retour == FALSE && nb_loop > 0)
	{
		// Passage au caractère suivant
		indice++;
		// On vérifie la taille de la future nouvelle combinaison
		if(indice <= maxLength - 1)
		{
			// Si on est au caractère de fin de chaine
			if(myStr[indice] == '\0')
			{
				// Ajout du nouveau caractère de fin de chaine
				myStr[indice+1] = '\0';
				// L'ancien caractère de fin de chaine devient donc le premier caractère du charset
				cs_indice[indice] = 0;
				(*myStrLength)++;
			}

			// L'indice du prochain caractère est alors "incrémenté" du nombre de boucles de charset effectué précédement.
			next_i_cs	= (cs_indice[indice] + nb_loop)%size_cs;
			// mise à jour du nombre de boucles
			nb_loop		= (cs_indice[indice] + nb_loop)/size_cs;

			// Mise à jour du caractère
			myStr[indice]		= charset[next_i_cs];
			cs_indice[indice]	= next_i_cs;
		}
		else
			retour = TRUE;
	}
	return retour;	// On renvoit le resultat
}


/**
 * Comparaison du md5 d'une chaine passée en paramètre avec celui que l'on essaye de craquer.
 * 
 * @param checkSum		Le md5 que l'on essaye de craquer.
 * @param myStr			La chaine dont on souhaite comparer le md5.
 * @param myStrLength	La taille de la chaine.
 */
int md5Compare(char * checkSum, char * myStr, int myStrLength)
{
	unsigned char	myStrDigest[16];
	int				retour = TRUE;
	int				i = 0;
	char			cmp[3];

	// Récupération du md5 vers le digest
	md5((unsigned char *) myStr, myStrLength, myStrDigest);

	while(i<16 && retour)
	{
		sprintf(cmp, "%02x", myStrDigest[i]);
		if(checkSum[2*i] != cmp[0] || checkSum[2*(i++)+1] != cmp[1])
			retour = FALSE;
	}
	return retour;
}

